﻿//sorting of moves
namespace FSharpChess

module Sort =
    //fruitcode
    let KillerScore = 4
    let TransScore = 32766
    let GoodScore = 4000
    let BadScore = -28000
    let HistoryScore = -24000
    let HistoryMax = 16384
    let HistorySize = 768
    let History = Array.create HistorySize 0
    let HistHit = Array.create HistorySize 0
    let HistTot = Array.create HistorySize 0
    exception InvalidMove
    let MovesMatch (moveA:Move) (moveB:Move) =
         if moveA.isBlank() || moveB.isBlank() then
             false 
         else
             moveA.mvPiece = moveB.mvPiece && moveA.From = moveB.From && moveA.To = moveB.To && moveA.mvName = moveB.mvName && (moveA.pieceCaptured = -1 && moveB.pieceCaptured = -1 || moveA.pieceCaptured <> -1 && moveB.pieceCaptured <> -1 && moveA.pieceCaptured = moveB.pieceCaptured)
    let history_index mv psn  =
        let pc = psn.Pcs.[psn.Sqs.[mv.From]]
        if pc.pcIsWhite then
            (int)pc.Name*64+mv.To
        else
            (6+(int)pc.Name)*64+mv.To
    let mvv_lva mv psn  =
        if mv.mvName=EnPassent then
            5
        elif mv.pieceCaptured<> -1 then
            let pc = psn.Pcs.[psn.Sqs.[mv.From]]
            let cap_pc = psn.Pcs.[mv.pieceCaptured]
            (int)cap_pc.Name*6-(int)pc.Name+5
        else
            //should be promotions
            match mv.mvName with
                |PawnPromotionQueen -> (int)enmName.Queen - 5
                |PawnPromotionRook -> (int)enmName.Rook - 5
                |PawnPromotionBishop -> (int)enmName.Bishop - 5
                |PawnPromotionKnight -> (int)enmName.Knight - 5
                |_ -> raise InvalidMove
    let capture_is_good mv psn  =
        // special cases
        if mv.mvName=EnPassent then
            true
        elif mv.mvName=PawnPromotionRook||mv.mvName=PawnPromotionBishop||mv.mvName=PawnPromotionKnight then
            false
        else
            // captures and queen promotes
            let pc = psn.Pcs.[psn.Sqs.[mv.From]]
            let cap_pc = psn.Pcs.[mv.pieceCaptured]
            if mv.pieceCaptured<> -1 then
                if mv.mvName=PawnPromotionQueen then
                    true
                else
                    let pc = psn.Pcs.[psn.Sqs.[mv.From]]
                    let cap_pc = psn.Pcs.[mv.pieceCaptured]
                    if (int)cap_pc.Name>=(int)pc.Name then
                        true
                    else
                        (SEE.see_move mv psn) >= 0
            else
                (SEE.see_move mv psn) >= 0
    let history_prob mv psn  =
        let index = history_index mv psn
        (HistHit.[index] * 16384) / HistTot.[index]                
    let move_value_simple (mv:Move) psn  =
        if mv.isTactical() then
            mvv_lva mv psn
        else
            HistoryScore
    let quiet_move_value mv psn  =
        let index = history_index mv psn
        HistoryScore + History.[index]
    let capture_value mv psn  =
        if capture_is_good mv psn then
            (mvv_lva mv psn) + GoodScore
        else
            (mvv_lva mv psn) + BadScore
    let move_value mv psn height trans_killer =
        if MovesMatch mv trans_killer then
            TransScore
        elif mv.isTactical() then
            capture_value mv psn
        elif MovesMatch mv (Kms.retrieveA(height)) then
            KillerScore
        elif MovesMatch mv (Kms.retrieveB(height)) then
            KillerScore - 1
        else
            quiet_move_value mv psn
    let note_mvv_lva (mvl:ResizeArray<Move>) psn =
        for i=0 to mvl.Count-1 do mvl.[i] <- {mvl.[i] with Score=mvv_lva mvl.[i] psn}
    let note_moves_simple (mvl:ResizeArray<Move>) psn =
        for i=0 to mvl.Count-1 do mvl.[i] <- {mvl.[i] with Score=move_value_simple mvl.[i] psn}
    let note_quiet_moves (mvl:ResizeArray<Move>) psn =
        for i=0 to mvl.Count-1 do mvl.[i] <- {mvl.[i] with Score=quiet_move_value mvl.[i] psn}
    let note_captures (mvl:ResizeArray<Move>) psn =
        for i=0 to mvl.Count-1 do mvl.[i] <- {mvl.[i] with Score=capture_value mvl.[i] psn}
    let note_moves (mvl:ResizeArray<Move>) psn height trans_killer =
        for i=0 to mvl.Count-1 do mvl.[i] <- {mvl.[i] with Score=move_value mvl.[i] psn height trans_killer}
    let history_bad (mv:Move) psn  =
        if not (mv.isTactical()) then
            let index = history_index mv psn
            HistTot.[index]<-HistTot.[index]+1
            if HistTot.[index] >= HistoryMax then
                HistHit.[index] <- (HistHit.[index] + 1) / 2
                HistTot.[index] <- (HistTot.[index] + 1) / 2
    let history_good (mv:Move) psn  =
        if not (mv.isTactical()) then
            let index = history_index mv psn
            HistTot.[index]<-HistTot.[index]+1
            HistHit.[index]<-HistHit.[index]+1
            if HistTot.[index] >= HistoryMax then
                HistHit.[index] <- (HistHit.[index] + 1) / 2
                HistTot.[index] <- (HistTot.[index] + 1) / 2
    let good_move (mv:Move) psn depth height =
        if not (mv.isTactical()) then
            if not (MovesMatch mv (Kms.retrieveA(height))) then
                Kms.assignB(height, Kms.retrieveA(height))
                Kms.assignA(height, mv)
            let index = history_index mv psn
            History.[index] <- History.[index] + depth*depth
            if History.[index] >= HistoryMax then
                for i = 0 to HistorySize-1 do History.[i] <- (History.[i] + 1) / 2
    let rec mvspFinAll player psn height mvsp i =
        if i < mvsp.Mvs.Count then
            false
        else
            match mvsp.State with
                |DoneAll -> true
                |DoneHash ->
                    let mvl = Move_gen.gen_lazy_moves_pr player psn
                    for mv in mvl do 
                        if mvsp.Mvs.Count<>0 && MovesMatch mv mvsp.Mvs.[0] then
                            ()
                        elif mv.pieceCaptured <> -1 then 
                            if capture_is_good mv psn then mvsp.Capl.Add(mv) else mvsp.Bcapl.Add(mv)
                        elif MovesMatch mv (Kms.retrieveA(height)) then
                            mvsp.KillA <- mv
                        elif MovesMatch mv (Kms.retrieveB(height)) then
                            mvsp.KillB <- mv
                        else
                            mvsp.Othl.Add(mv)
                    note_mvv_lva mvsp.Capl psn
                    mvsp.Capl.Sort(Funcs2.CompareMove)
                    mvsp.Mvs.AddRange(mvsp.Capl)
                    mvsp.State<-DoneGoodCaptures
                    mvspFinAll player psn height mvsp i
                |DoneGoodCaptures ->
                    note_mvv_lva mvsp.Bcapl psn
                    mvsp.Bcapl.Sort(Funcs2.CompareMove)
                    mvsp.Mvs.AddRange(mvsp.Bcapl)
                    mvsp.State<-DoneBadCaptures
                    mvspFinAll player psn height mvsp i
                |DoneBadCaptures ->
                    if not (mvsp.KillA.isBlank()) then mvsp.Mvs.Add(mvsp.KillA)
                    mvsp.State<-DoneKillA
                    mvspFinAll player psn height mvsp i
                |DoneKillA ->
                    if not (mvsp.KillB.isBlank()) then mvsp.Mvs.Add(mvsp.KillB)
                    mvsp.State<-DoneKillB
                    mvspFinAll player psn height mvsp i
                |DoneKillB ->
                    note_quiet_moves mvsp.Othl psn
                    mvsp.Othl.Sort(Funcs2.CompareMove)
                    mvsp.Mvs.AddRange(mvsp.Othl)
                    mvsp.State<-DoneAll
                    mvspFinAll player psn height mvsp i
                |_ -> raise InvalidMpState
    let rec mvspFinCaps player psn mvsp i =
        if i < mvsp.Mvs.Count then
            false
        else
            match mvsp.State with
                |DoneAll -> true
                |DoneHash ->
                    let mvl = Move_gen.gen_captures_pr player psn
                    if mvsp.Mvs.Count=0 then
                        for mv in mvl do 
                            if capture_is_good mv psn then mvsp.Capl.Add(mv) 
                    else
                        let moveHash = mvsp.Mvs.[0]
                        for mv in mvl do 
                            if not (MovesMatch mv moveHash) && capture_is_good mv psn then mvsp.Capl.Add(mv) 
                    note_mvv_lva mvsp.Capl psn
                    mvsp.Capl.Sort(Funcs2.CompareMove)
                    mvsp.Mvs.AddRange(mvsp.Capl)
                    mvsp.State<-DoneAll
                    mvspFinCaps player psn mvsp i
                |_ -> raise InvalidMpState
    let rec mvspFinEvas player psn checks mvsp i =
        if i < mvsp.Mvs.Count then
            false
        else
            match mvsp.State with
                |DoneAll -> true
                |DoneHash ->
                    let mvl = Move_gen.gen_evasions_pr player psn checks
                    if mvsp.Mvs.Count=0 then
                        mvsp.Evasl.AddRange(mvl)
                    else
                        let moveHash = mvsp.Mvs.[0]
                        for mv in mvl do 
                            if not (MovesMatch mv moveHash) then mvsp.Evasl.Add(mv) 
                    note_moves_simple mvsp.Evasl psn
                    mvsp.Evasl.Sort(Funcs2.CompareMove)
                    mvsp.Mvs.AddRange(mvsp.Evasl)
                    mvsp.State<-DoneAll
                    mvspFinEvas player psn checks mvsp i
                |_ -> raise InvalidMpState
    let mvspFin player psn checks height mvsp i =
        match mvsp.GnType with
            |GenAll -> mvspFinAll player psn height mvsp i
//            |GenEvasions -> mvspFinAll player psn height mvsp i
            |GenCaptures -> mvspFinCaps player psn mvsp i
            |GenEvasions -> mvspFinEvas player psn checks mvsp i
    let mvsPossF player psn gntyp height checks =
        let movesPossible = MvsPoss.newtyp(gntyp)
        movesPossible.State<-DoneHash
        //just get hash table value - get others if needed later
        let moveHash = Bms.ProbeForBestMove(player.prIsWhite,Ref.FiftyMoveDrawBase,psn)
        if not (moveHash.isBlank()) then
            movesPossible.Mvs.Add(moveHash)
            movesPossible
        else
            mvspFin player psn checks height movesPossible 0 |>ignore
            movesPossible
             
 